﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Diagnostics.Metrics;
using System.Drawing;
using System.IO.Ports;
using xiaoxiong.ScadaProModbusRtu.Message;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using thinger.CommunicationLib.Helper;
using thinger.CommunicationLib.StoreArea;
using thinger.ConfigLib;
using thinger.DataConvertLib;
using xiaoxiong.KYJControllersLib;
using xiaoxiong.KYJDAL.Models;
using xiaoxiong.ScadaProModbusRtu.Common;
using xiaoxiong.KYJDAL.Services;

namespace xiaoxiong.ScadaProModbusRtu
{
    public partial class FrmMain : Form
    {
        //窗体拖动事件
        private bool _isDragging = false;
        private Point _startPoint = new Point(0, 0);
        public FrmMain()
        {
            InitializeComponent();

            //实例化定时存储定时器
            storeTimer = new System.Timers.Timer();
            storeTimer.Interval = 1000;
            storeTimer.AutoReset = true;
            storeTimer.Elapsed += StoreTimer_Elapsed;

            actualAlarmList.CollectionChanged += ActualAlarmList_CollectionChanged;

            this.FormClosing += FrmMain_FormClosing;
        }

        //关闭定时存储器
        private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            storeTimer.Stop();
        }

        //实时数据业务
        private ActualDataService ActualDataService = new ActualDataService();
        private MysqlService mysqlService = new MysqlService();
        //所有归档变量名称集合
        private List<string> varNameList;
        private List<string> varValueList;
        private void StoreTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (CommonMethods.plcDevice.IsConnected && mysqlService.IsTableExits("actualdata") 
                )
            {
                varValueList = new List<string>() {CommonMethods.CurrentTime};
                varValueList.AddRange(CommonMethods.plcDevice.StoreVarList.Select(c => c.VarValue?.ToString()).ToList());

                try
                {
                    var reslut = ActualDataService.InsertActual(varNameList, varValueList);
                    if (!reslut.IsSuccess)
                    {
                        CommonMethods.AddLog(true, reslut.Message);
                    }
                }
                catch(Exception ex)
                {
                    CommonMethods.AddLog(true, "实时存储出错" + ex.Message);
                }
            }
        }


        /// <summary>
        /// 集合变化时会触发此事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void ActualAlarmList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.Invoke(new Action(() =>
            {
                //设置
                switch (actualAlarmList.Count)
                {
                    case 0:
                        this.AlarmPanel.Visible = false;
                        break;
                    case 1:
                        this.AlarmPanel.Visible = true;
                        this.scrollText1.TextScroll = string.Join(" ", this.actualAlarmList);
                        this.scrollText1.IsScoll = false;
                        break;
                    default:
                        this.AlarmPanel.Visible = true;
                        this.scrollText1.TextScroll = string.Join(" ", this.actualAlarmList);
                        this.scrollText1.IsScoll = true;
                        break;
                }
            }));
        }

        private Action<SysLog> AddAlarmDelegate;

        //实时报警集合
        private ObservableCollection<string> actualAlarmList = new ObservableCollection<string>();

        //创建消息过滤对象
        private MessageFilter messageFilter;

        //配置文件路径
        private string xmlPath = Application.StartupPath + @"\settings\settings.json";
        //通信取消
        private CancellationTokenSource cts;

        //定时存储定时器
        private System.Timers.Timer storeTimer;

        private void FrmMain_Load(object sender, EventArgs e)
        {
            //设置登录用户名
            this.lbl_LoginName.Text = CommonMethods.CommonAdmin.LoginName;

            //先打开日志信息
            OpenWindow(FormNames.日志报警);
            OpenWindow(FormNames.控制流程);


            //登录成功
            CommonMethods.AddOpLog(false, $"用户{this.lbl_LoginName.Text}登录成功");

            //自动锁屏
            if (CommonMethods.sysConfig.AutoLock)
            {
                messageFilter = new MessageFilter();
                Application.AddMessageFilter(messageFilter);
            }

            //读取变量配置信息
            var result = GetDeviceByPath(xmlPath);

            if (result.IsSuccess)
            {
                CommonMethods.plcDevice = result.Content;

                CommonMethods.plcDevice.PortName = "Com2";

                //设置成  长地址模型
                CommonMethods.plcDevice.IsShortAddress = false;
                //设置大小端
                CommonMethods.plcDevice.DataFormat = (int)DataFormat.CDAB;
                CommonMethods.plc.DataFormat = DataFormat.CDAB;

                CommonMethods.plcDevice.Init();

                //初始化 varNameList
                varNameList = new List<string>() { "InsertTime" };
                varNameList.AddRange(CommonMethods.plcDevice.StoreVarList.Select(c => c.VarName).ToList());


                CommonMethods.plcDevice.Cts = new CancellationTokenSource();

                //报警事件
                CommonMethods.plcDevice.AlarmTriggerEvent += PlcDevice_AlarmTriggerEvent;

                Task.Run( () =>
                {
                    PLCCommunication(CommonMethods.plcDevice);
                }, CommonMethods.plcDevice.Cts.Token);


                //开始定时存储
                storeTimer.Start();
            }
        }

        /// <summary>
        /// 报警触发事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PlcDevice_AlarmTriggerEvent(object sender, AlarmEventArgs e)
        {
            AddAlarm(new SysLog()
            {
                InsertTime = CommonMethods.CurrentTime,
                LogType = "系统报警",
                Operator = CommonMethods.CommonAdmin.LoginName,
                VarName = e.Name,
                AlarmSet = e.SetValue,
                AlarmValue = e.CurrentValue,
                AlarmType = e.IsTrigger ? "触发" : "消除"
            });

            if (e.IsTrigger)
            {
                if (this.actualAlarmList.Contains(e.AlarmNote))
                {
                    actualAlarmList.Add(e.AlarmNote);
                }
            }
            //消除报警
            else
            {
                if (this.actualAlarmList.Contains(e.AlarmNote))
                {
                    actualAlarmList.Remove(e.AlarmNote);
                }
            }
        }

        /// <summary>
        /// 多线程读取数据
        /// </summary>
        /// <param name="plcDevice"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void PLCCommunication(ModbusRTUDevice device)
        {
            while (!device.Cts.Token.IsCancellationRequested)
            {
                if (device.IsConnected == true)
                {
                    //读取
                    foreach (var group in device.GroupList)
                    {
                        if (group.IsActive)
                        {
                            var result = GetGroupValue(device,group);
                            //如果读取成功

                            if (result.IsSuccess)
                            {
                                SetConnectState(true);
                                //设置通信状态
                                device.ErrorTimes = 0;
                            }
                            else
                            {
                                device.ErrorTimes++;
                                if (device.ErrorTimes >= device.AllowErrorTimes)
                                {
                                    SetConnectState(false);

                                    //设置通信状态
                                    if (IsComExits(device.PortName))
                                    {
                                        Task.Delay(10);
                                        continue;
                                    }
                                    else
                                    {
                                        device.IsConnected = false;
                                        //写入日志
                                        CommonMethods.AddLog(true, "通信读取出错：" + result.Message);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    //连接
                    if (device.IsFirstConnect == false)
                    {
                        Thread.Sleep(device.ReConnectTime);
                        CommonMethods.plc?.DisConnect();
                    }

                    //连接
                    bool result = CommonMethods.plc.Connect(device.PortName);

                    if (result)
                    {
                        device.IsConnected = true;
                        SetConnectState(true);
                        //通信状态 写入日志
                        CommonMethods.AddLog(false, device.IsFirstConnect? "PLC初次连接成功" : "PLC重新连接成功");
                    }
                    else
                    {
                        //写入日志
                        device.IsConnected = false;
                        SetConnectState(false);
                        CommonMethods.AddLog(true, device.IsFirstConnect ? "PLC初次连接失败" : "PLC重新连接失败");
                    }
                    //清除首次连接标志位
                    if (device.IsFirstConnect)
                    {
                        device.IsFirstConnect = false;
                    }
                }
            }
        }


        /// <summary>
        /// 实时的报警日志
        /// </summary>
        private void AddAlarm(SysLog log)
        {
            if (AddAlarmDelegate != null)
            {
                AddAlarmDelegate?.Invoke(log);
            }
        }        


        /// <summary>
        /// 解析变量的起始地址
        /// </summary>
        /// <param name="start"></param>
        /// <param name="fromBase"></param>
        /// <returns></returns>
        private OperateResult<int,int> AnalysisStartAddress(string start,int fromBase)
        {
            try
            {
                if (start.Contains("."))
                {
                    string[] result = start.Split('.');
                    if (result.Length == 2)
                    {
                        return OperateResult.CreateSuccessResult(Convert.ToInt32(result[0]),
                            Convert.ToInt32(result[1]));
                    }
                    else
                    {
                        return OperateResult.CreateFailResult<int, int>(new OperateResult("起始地址填写不正确:" + start));
                    }
                }
                else if (start.Contains("|"))
                {
                    string[] result = start.Split('|');
                    if (result.Length == 2)
                    {
                        return OperateResult.CreateSuccessResult(Convert.ToInt32(result[0]),
                            Convert.ToInt32(result[1]));
                    }
                    else
                    {
                        return OperateResult.CreateFailResult<int, int>(new OperateResult("起始地址填写不正确:" + start));
                    }
                }
                else
                {
                    return OperateResult.CreateSuccessResult(Convert.ToInt32(start, fromBase), 0);
                }
            }
            catch(Exception ex)
            {
                return OperateResult.CreateFailResult<int, int>(new OperateResult($"起始地址{start}转换不正确:" + ex.Message));
            }      
        }


        /// <summary>
        /// 读取单个通信组
        /// </summary>
        /// <returns></returns>
        private OperateResult GetGroupValue(ModbusRTUDevice device,ModbusRTUGroup group)
        {
            //解析起始地址
            var addResult = ModbusHelper.ModbusAddressAnalysis(group.Start,
                0, device.IsShortAddress);
            if (addResult.IsSuccess == false) return OperateResult.CreateFailResult(addResult.Message);
            if (addResult.Content1 == ModbusStoreArea.X3 || 
                addResult.Content1 == ModbusStoreArea.X4)
            {
                var result = CommonMethods.plc.ReadByteArray(group.Start, (ushort)group.Length);
                if (result.IsSuccess)
                {
                    //解析
                    if (result.Content.Length == group.Length * 2)
                    {
                        //遍历通信组每一个变量
                        foreach (var variable in group.VarList)
                        {
                            var analysis = AnalysisStartAddress(variable.Start,10);
                            if (analysis.IsSuccess)
                            {
                                int start = analysis.Content1;
                                int offset = analysis.Content2;

                                start = start - addResult.Content3;
                                start = start * 2;

                                switch (variable.VarType.ToUpper())
                                {
                                    case "BOOL":
                                        variable.VarValue = BitLib.GetBitFrom2BytesArray(result.Content,start,offset,(DataFormat)device.DataFormat
                                            == DataFormat.ABCD || (DataFormat)device.DataFormat == DataFormat.CDAB);
                                        break;
                                    case "BYTE":
                                        variable.VarValue = ByteLib.GetByteFromByteArray(result.Content,(DataFormat)device.DataFormat
                                            == DataFormat.ABCD || (DataFormat)device.DataFormat == DataFormat.CDAB ? start : start + 1);
                                        break;
                                    case "USHORT":
                                        variable.VarValue = UShortLib.GetUShortFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "SHORT":
                                        variable.VarValue = ShortLib.GetShortFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "INT":
                                        variable.VarValue = IntLib.GetIntFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "UINT":
                                        variable.VarValue = UIntLib.GetUIntFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "LONG":
                                        variable.VarValue = LongLib.GetLongFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "ULONG":
                                        variable.VarValue = ULongLib.GetULongFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "FLOAT":
                                        variable.VarValue = FloatLib.GetFloatFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "DOUBLE":
                                        variable.VarValue = DoubleLib.GetDoubleFromByteArray(result.Content, start, (DataFormat)device.DataFormat);
                                        break;
                                    case "STRING":
                                        variable.VarValue = StringLib.GetStringFromByteArrayByEncoding(result.Content, start,offset,Encoding.ASCII);
                                        break;
                                    case "BYTEARRAY":
                                        variable.VarValue = ByteArrayLib.GetByteArrayFromByteArray(result.Content, start, offset);
                                        break;
                                    case "HEXSTRING":
                                        variable.VarValue = StringLib.GetHexStringFromByteArray(result.Content, start, offset);
                                        break;
                                    default:
                                        break;
                                }

                                //线性转换
                                var operateResult = MigrationLib.GetMigrationValue(variable.VarValue,variable.Scale.ToString(),variable.Offset.ToString());
                            
                                if (operateResult.IsSuccess)
                                {
                                    variable.VarValue = operateResult.Content;
                                }
                                else
                                {
                                    variable.VarValue = null;
                                    CommonMethods.AddLog(true,"线性转换失败" + operateResult.Message);
                                }

                                //更新变量
                                device.Update(variable);
                            }
                            else
                            {
                                return OperateResult.CreateFailResult(analysis.Message);
                            }
                        }
                    }
                    else
                    {
                        return OperateResult.CreateFailResult($"读取长度:({result.Content.Length})" +
                            $"与通讯组长度({group.Length})不一致");
                    }
                }
                else
                {
                    //读取失败
                    return OperateResult.CreateFailResult(result.Message);
                }
            }
            else
            {
                var result = CommonMethods.plc.ReadBoolArray(group.Start, (ushort)group.Length);
                if (result.IsSuccess)
                {
                    //解析
                    if (result.Content.Length == group.Length)
                    {
                        //遍历通信组每一个变量
                        foreach (var variable in group.VarList)
                        {
                            var analysis = AnalysisStartAddress(variable.Start, 10);
                            if (analysis.IsSuccess)
                            {
                                int start = analysis.Content1;

                                start = start - addResult.Content3;

                                switch (variable.VarType.ToUpper())
                                {
                                    case "BOOL":
                                        variable.VarValue = result.Content[start];
                                        break;       
                                    default:
                                        break;
                                }      
                                //更新变量
                                device.Update(variable);
                            }
                            else
                            {
                                return OperateResult.CreateFailResult(analysis.Message);
                            }
                        }
                    }
                    else
                    {
                        return OperateResult.CreateFailResult($"读取长度:({result.Content.Length})" +
                            $"与通讯组长度({group.Length})不一致");
                    }
                }
                else
                {
                    //读取失败
                    return OperateResult.CreateFailResult(result.Message);
                }
            }
            return OperateResult.CreateSuccessResult();
        }

        /// <summary>
        /// 设置连接状态
        /// </summary>
        /// <param name="state"></param>
        private void SetConnectState(bool state)
        {
            if (this.led_State.InvokeRequired)
            {
                this.led_State.Invoke(new Action<bool>(SetConnectState),state);
            }
            else
            {
                this.led_State.State = state;
            }
        }

        /// <summary>
        /// 判断串口号是否存在
        /// </summary>
        /// <param name="portName"></param>
        /// <returns></returns>
        private bool IsComExits(string portName)
        {
            List<string> result = new List<string>();
            try
            {
                // 查询设备管理器中所有 PnP 实体
                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity"))
                {
                    ManagementObjectCollection hardInfos = searcher.Get();
                    foreach (ManagementObject hardInfo in hardInfos)
                    {
                        if (hardInfo["Name"] != null)
                        {
                            string name = hardInfo["Name"].ToString();
                            // 筛选名称包含 "(COM" 且以 ")" 结尾的设备
                            if (name.Contains("(COM") && name.EndsWith(")"))
                            {
                                int startIndex = name.IndexOf('(') + 1;
                                int endIndex = name.IndexOf(')');
                                string comPort = name.Substring(startIndex, endIndex - startIndex);

                                // 处理包含 "->" 的特殊情况（如虚拟串口）
                                if (comPort.Contains("->"))
                                {
                                    comPort = comPort.Split(new[] { "->" }, StringSplitOptions.None)[0];
                                }
                                result.Add(comPort.Trim().ToUpper()); // 统一转为大写
                            }
                        }
                    }
                }
            }
            catch
            {
                // 可在此记录异常日志
                return false;
            }
            // 判断目标端口是否存在（不区分大小写）
            return result.Exists(p => p.Equals(portName.ToUpper()));
        }
        /// <summary>
        /// 配置文件解析为对象
        /// </summary>
        /// <param name="xmlPath"></param>
        /// <returns></returns>
        private OperateResult<ModbusRTUDevice> GetDeviceByPath(string xmlPath)
        {
            List<Project> projects = new ConfigManage().LoadProjects(xmlPath);
            if (projects.Count > 0 && projects != null)
            {
                Project project = projects[0];
                if (project.ModbusRTUList.Count > 0)
                {
                    var device = project.ModbusRTUList[0];
                    if (device.IsActive)
                    {
                        return OperateResult.CreateSuccessResult(device);
                    }
                    else
                    {
                        return OperateResult.CreateFailResult<ModbusRTUDevice>(new OperateResult("请检查配置是否激活"));
                    }
                }
                else
                {
                    return OperateResult.CreateFailResult<ModbusRTUDevice>(new OperateResult("请检查配置是否含有ModbusRtu设备"));
                }
            }
            else
            {
                return OperateResult.CreateFailResult<ModbusRTUDevice>(new OperateResult("请检查配置文件是否正确"));
            }
        }

         
        private bool naviPanel1_ButtonSelectEvent(object sender, EventArgs e)
        {
            if (sender is FormNames formNames)
            {
                //权限判断
                switch (formNames)
                {
                    case FormNames.控制流程:
                        break;
                    case FormNames.日志报警:
                        break;
                    case FormNames.临界窗体:
                        break;
                    case FormNames.日志查询:
                        break;
                    case FormNames.参数设置:
                        break;
                    case FormNames.实时趋势:
                        break;
                    case FormNames.历史趋势:
                        break;
                    case FormNames.数据报表:
                        break;
                    case FormNames.用户管理:
                        break;
                    case FormNames.退出系统:
                        this.Close();
                        break;
                    default:
                        break;
                }
                //打开窗体
                OpenWindow(formNames);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 通用打开方法
        /// </summary>
        /// <param name="formNames"></param>
        private void OpenWindow(FormNames formNames)
        {
            int total = this.MainPanel.Controls.Count;
            int closeCount = 0;
            bool isFind = false;
            for (int i = 0; i < total; i++)
            {
                Control ct = this.MainPanel.Controls[i - closeCount];
                if (ct is Form frm)
                {
                    if (frm.Text == formNames.ToString())
                    {
                        frm.BringToFront();
                        isFind = true;
                    }
                    else if ((FormNames)Enum.Parse(typeof(FormNames),frm.Text) >= FormNames.临界窗体)
                    {
                        frm.Close();
                        closeCount++;
                    }
                }
            }

            if (isFind == false)
            {
                Form frm = null;
                switch (formNames)
                {   
                    case FormNames.控制流程:
                        frm = new FrmMonitor();
                        break;
                    case FormNames.日志报警:
                        frm = new FrmAlarmLog();
                        CommonMethods.AddLogDelegate = (frm as FrmAlarmLog).AddLog;
                        CommonMethods.AddOpLogDelegate = (frm as FrmAlarmLog).AddOPLog;
                        AddAlarmDelegate = (frm as FrmAlarmLog).AddAlarmLog;
                        break;
                    case FormNames.临界窗体:
                        break;
                    case FormNames.日志查询:
                        frm = new FrmHistoryLog();
                        break;
                    case FormNames.参数设置:
                        frm = new FrmParamset();
                        break;
                    case FormNames.实时趋势:
                        frm = new FrmActualTrend();
                        break;
                    case FormNames.历史趋势:
                        frm = new FrmHistoryTrend();
                        break;
                    case FormNames.数据报表:
                        frm = new FrmReport();
                        break;
                    case FormNames.用户管理:
                        frm = new FrmUserManager();
                        break;
                    default:
                        break;
                }

                if (frm != null)
                {
                    //非置顶控件
                    frm.TopLevel = false;
                    //去边框
                    frm.FormBorderStyle = FormBorderStyle.None;
                    //设置填充
                    frm.Dock = DockStyle.Fill;
                    //设置父容器
                    frm.Parent = this.MainPanel;
                    frm.BringToFront();
                    frm.Show();
                }
            }
        }

        #region 无边框拖动
        private void TopPanel_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _isDragging = true;
                _startPoint = new Point(e.X, e.Y);
            }
        }

        private void TopPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isDragging)
            {
                Point newPoint = this.PointToScreen(new Point(e.X, e.Y));
                this.Location = new Point(newPoint.X - _startPoint.X, newPoint.Y - _startPoint.Y);
            }
        }

        private void TopPanel_MouseUp(object sender, MouseEventArgs e)
        {
            _isDragging = false;
        }
        #endregion

 
    }
}
